package com.aisafer.fms.client.service;

import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.aisafer.base.enums.ResponseEnum;
import com.aisafer.base.exception.ServiceException;
import com.aisafer.base.pojo.Response;
import com.aisafer.fms.client.dto.AttachmentContent;
import com.aisafer.fms.client.dto.AttachmentGroupReq;
import com.aisafer.fms.client.dto.AttachmentGroupResp;
import com.aisafer.fms.client.dto.AttachmentReq;
import com.aisafer.fms.client.dto.AttachmentResp;
import com.aisafer.fms.client.dto.DownGroupReq;
import com.aisafer.fms.client.dto.DownReq;
import com.aisafer.fms.client.dto.DownResp;
import com.aisafer.fms.client.dto.GroupReq;

/**
 * 附件分片上传
 * @author stevin
 */
public class AttachmentBlockHandlerImpl implements AttachmentBlockHandler{
	
	private static final Logger logger = LoggerFactory.getLogger(AttachmentBlockHandlerImpl.class);

	private AttachmentService ossService;

	private AttachmentGroupService attachmentGroupService;
	
	public AttachmentBlockHandlerImpl() {
		
	}	
	
	public AttachmentBlockHandlerImpl(AttachmentService ossService, AttachmentGroupService attachmentGroupService) {
		this.ossService = ossService;
		this.attachmentGroupService = attachmentGroupService;
	}

	@Override
	public Response<AttachmentResp> uploadFile(final AttachmentReq req,final GroupReq groupReq) {
		Response<AttachmentResp> response = new Response<>();
		if(req.getAttachmentContent() ==null || req.getAttachmentContent().getContent() == null || req.getAttachmentContent().getContent().length <= 0) {
			return null;
		}
		ExecutorService executor = null;
		try {
			executor = Executors.newFixedThreadPool(groupReq.getConcurrentNum());
			long begin = System.currentTimeMillis();
			List<AttachmentGroupReq> resultList = new CopyOnWriteArrayList<>();
			final Long groupid = System.currentTimeMillis();
			final int blockNum = new Block().getBlockNum(req.getAttachmentContent().getContent().length, groupReq.getBlockSize());
			logger.info("uploadFileGroup.time1:{}",(System.currentTimeMillis() - begin));
			CountDownLatch answer = new CountDownLatch(blockNum);
			final long begin2 = System.currentTimeMillis();
			for(int i=1;i<=blockNum;i++) {
				uploadBlockFile(req, i, groupReq,groupid,executor,answer,resultList);
			}
			if(logger.isDebugEnabled()) {
				logger.debug("uploadFileGroup.time2:{}",(System.currentTimeMillis() - begin2));
			}
			answer.await(groupReq.getTimeout(), TimeUnit.SECONDS);
			long begin3 = System.currentTimeMillis();
			if(blockNum != resultList.size()) {
				logger.info("blockNum:{},blockUploadNum:{}",blockNum,resultList.size());
				final AttachmentResp resp = new AttachmentResp();
				response.setCode(ResponseEnum.SUCESS200.getCode());
				resp.setAttachid(0l);
				response.setData(resp);
				return response;
			}
			else {
				addBathGroup(resultList);
			}
			if(logger.isDebugEnabled()) {
				logger.debug("uploadFileGroup.time3:{}",(System.currentTimeMillis() - begin3));
			}
			final AttachmentResp resp = new AttachmentResp();
			response.setCode(ResponseEnum.SUCESS200.getCode());
			resp.setAttachid(groupid);
			response.setData(resp);
			if(logger.isDebugEnabled()) {
				logger.debug("result.size:{}",resultList.size());
			}
			logger.info("uploadFileGroup.groupid:{},time:{}",groupid,(System.currentTimeMillis() - begin));
		} catch (Exception  e) {
			e.printStackTrace();
		}
		return response;
	}
	
	public  String getUUid() {
		return UUID.randomUUID().toString().replace("-", "").toLowerCase();
	}
	
	private void addBathGroup(List<AttachmentGroupReq> resultList) {
		final Response<AttachmentGroupResp> respon = attachmentGroupService.insertGroup(resultList);
		if(logger.isDebugEnabled()) {
			logger.debug("respons:{}",respon);
		}
	}
	
	private void uploadBlockFile(AttachmentReq req,int curBlock,GroupReq groupReq,final Long groupid,ExecutorService executor,CountDownLatch answer,List<AttachmentGroupReq> resultList) {
		UploadThread uploadThread = new UploadThread(req,curBlock,groupReq,groupid,answer,resultList);
		executor.execute(uploadThread);
	}
	
	class UploadThread implements Runnable{
		
		private AttachmentReq req;
		
		private int curBlock;
		
		private GroupReq groupReq;
		
		private Long groupid;
		
		CountDownLatch answer;
		
		List<AttachmentGroupReq> resultList;
		
		public UploadThread(AttachmentReq req,int curBlock,GroupReq groupReq,final Long groupid,CountDownLatch answer,List<AttachmentGroupReq> resultList) {
			this.req = req;
			this.curBlock = curBlock;
			this.groupReq = groupReq;
			this.groupid = groupid;
			this.answer = answer;
			this.resultList = resultList;
		}
		
		@Override
		public void run() {
			try {
				long begin = System.currentTimeMillis();
				byte[] content = new Block().getSplitBlock(req.getAttachmentContent().getContent(), curBlock, groupReq);
				if(content == null) {
					return;
				}
				
				AttachmentReq attachmentReq = new AttachmentReq();
				AttachmentContent attachmentContent = new AttachmentContent();
				attachmentReq.setModuleKey(req.getModuleKey());
				attachmentContent.setContent(content);
				attachmentContent.setFileType(req.getAttachmentContent().getFileType());
				attachmentContent.setFileName(req.getAttachmentContent().getFileName());
				attachmentContent.setFileSize(Integer.valueOf(content.length).longValue());
				attachmentContent.setSourceCode(req.getAttachmentContent().getSourceCode());
				attachmentReq.setAttachmentContent(attachmentContent);
				long begin2 = System.currentTimeMillis();
				Response<AttachmentResp> result =  ossService.uploadFile(attachmentReq);
				logger.info("upload.remote.file.time:{},result:{}",(System.currentTimeMillis() - begin2),result);
				AttachmentGroupReq attachmentGroupReq = new AttachmentGroupReq();
				attachmentGroupReq.setAttachid(result.getData().getAttachid());
				attachmentGroupReq.setFileBlock(curBlock);
				attachmentGroupReq.setGroupid(groupid);
				resultList.add(attachmentGroupReq);
				logger.info("Upload.total.time:{}",(System.currentTimeMillis() - begin));
			}finally {
				answer.countDown();
			}
		}

		public AttachmentReq getReq() {
			return req;
		}

		public void setReq(AttachmentReq req) {
			this.req = req;
		}

		public long getCurBlock() {
			return curBlock;
		}

		public void setCurBlock(int curBlock) {
			this.curBlock = curBlock;
		}
		
	}

	@Override
	public byte[] downloadFileByte(final DownGroupReq req) throws ServiceException {
		try {
			final long begin = System.currentTimeMillis();
			Map<Integer,byte[]> blockAttachmentMap = new LinkedHashMap<>();
			ExecutorService executor = Executors.newFixedThreadPool(req.getConcurrentNum());
			AttachmentGroupReq attachmentGroupReq = new AttachmentGroupReq();
			attachmentGroupReq.setGroupid(req.getAttachid());
			List<AttachmentGroupResp> list = attachmentGroupService.getList(attachmentGroupReq);
			CountDownLatch answer = new CountDownLatch(list.size());
			for(AttachmentGroupResp resp : list) {
				GroupDownThread uploadThread = new GroupDownThread(resp,answer,blockAttachmentMap);
				executor.execute(uploadThread);
			}
			answer.await(req.getTimeout(), TimeUnit.SECONDS);
			final byte[] contentByte = new Block().mergeByteArray(blockAttachmentMap);
			//兼容老附件上传方式
			if(contentByte == null || contentByte.length <=0) {
				DownReq downReq = new DownReq();
				downReq.setAttachid(req.getAttachid());
				return ossService.downloadFileByte(downReq);
			}
			logger.info("downloadFileByte.time:{}",(System.currentTimeMillis() - begin));
		    return contentByte;   
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		return null;
	}
	
	private class GroupDownThread implements Runnable{

		private AttachmentGroupResp attachmentGroupResp;
		
		CountDownLatch answer;
		
		Map<Integer,byte[]> blockAttachmentMap;
		
		public GroupDownThread(AttachmentGroupResp attachmentGroupResp,CountDownLatch answer,Map<Integer,byte[]> blockAttachmentMap) {
			this.attachmentGroupResp = attachmentGroupResp;
			this.answer = answer;
			this.blockAttachmentMap = blockAttachmentMap;
		}
		
		@Override
		public void run() {
			try {
				DownReq req = new DownReq();
				req.setAttachid(attachmentGroupResp.getAttachid());
				byte[] by = ossService.downloadFileByte(req);
				blockAttachmentMap.put(attachmentGroupResp.getFileBlock(), by);
			}finally{
				answer.countDown();
			}
		}
	}

	@Override
	public DownResp download(DownGroupReq req) throws ServiceException {
		return null;
	}

	public AttachmentService getOssService() {
		return ossService;
	}

	public void setOssService(AttachmentService ossService) {
		this.ossService = ossService;
	}

	public AttachmentGroupService getAttachmentGroupService() {
		return attachmentGroupService;
	}

	public void setAttachmentGroupService(AttachmentGroupService attachmentGroupService) {
		this.attachmentGroupService = attachmentGroupService;
	}
	
	
}
